package utils.mathCalc;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.NavigableMap;
import java.util.Set;
import java.util.Map.Entry;

import objects.base.Document;
import objects.base.WordData;

import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;


import utils.primitif.StringUtils;




public class Calculations {
	public static void calculFrequencies(Hashtable<String,WordData> hashStringToWord,
			Hashtable<String,Integer> hashDateCount,
			Hashtable<String,Set<Document>> hashDateAllDocument){

		Iterator<WordData> itValue = hashStringToWord.values().iterator(); 
		Iterator<String> itKeyWord = hashStringToWord.keySet().iterator(); 
		String sCurrentWord;
		while(itValue.hasNext()){ 
			sCurrentWord = itKeyWord.next();
			WordData value = itValue.next();
			Hashtable<Date, Set<Document>> hashWord = value.getHashWord();
			int activityVotePerDocument = 0;
			Iterator<Set<Document>> valueSet = hashWord.values().iterator(); 
			Iterator<Date> itKey = hashWord.keySet().iterator();

			while(itKey.hasNext()){ 
				double frequency = 0;
				int sumVoteOpinion = 0;
				Set<Document> set = valueSet.next();
				Date d = itKey.next();
				Iterator<Document> i = set.iterator(); 
				String dateS = d.toString();
				Set<Document> documentSet = hashDateAllDocument.get(dateS); 

				while(i.hasNext())
				{
					Document current = i.next();
					if(documentSet.contains(current))
						activityVotePerDocument += current.getVoteOrOpinionCount();
				}

				for(Document doc: documentSet){
					sumVoteOpinion += doc.getVoteOrOpinionCount();
				}
				frequency = (set.size() / (double)hashDateCount.get(dateS)) + (activityVotePerDocument/(double)sumVoteOpinion);
				value.putFrequencies(d,frequency);
				value.setLastFrequency(frequency);

				/*System.out.println("------------------------------------------------------");
				System.out.println(sCurrentWord);
				System.out.println("frequency = " + frequency);
				System.out.println("date = " +dateS);
				System.out.println("sumVoteOpinion = " +sumVoteOpinion);
				System.out.println("activityVotePerDocument = " +activityVotePerDocument);
				System.out.println("hashDateCount = " +hashDateCount);
				System.out.println("date number doc = " +hashDateCount.get(dateS));
				System.out.println("set size = " + set.size());
				System.out.println("hash frequency current word" + value.getHashfrequencies());*/


				hashStringToWord.put(sCurrentWord, value);
				activityVotePerDocument = 0;
			} 
		}
	}

	public static void calculStandardDeviation(Hashtable<String,WordData> hashStringToWord,Date d){
		Iterator<WordData> itValue = hashStringToWord.values().iterator(); 
		while(itValue.hasNext()){ 
			WordData value = itValue.next();
			if(value.frequenciesDoubleArrayFromHash().length <= 1){
				continue;
			}

			org.apache.commons.math.stat.descriptive.moment.StandardDeviation stDev = new StandardDeviation();
			org.apache.commons.math.stat.descriptive.moment.Mean stMean = new Mean();
			double frTab[] = value.frequenciesDoubleArrayFromHash();
			value.setMean(stMean.evaluate(value.frequenciesDoubleArrayFromHash()));

			double standardDeviation = stDev.evaluate(frTab,value.getMean());
			double zScore = (value.getLastFrequency() - value.getMean()) / standardDeviation;
			value.putZscore(d, zScore);
			if(Math.abs(zScore) > 4){
				WordData.puthashTrendDate(d.toString(), value.getRealWord());
				/*System.out.println("---------------NEW DAY--------------");
				System.out.println("word= " +value.getWord() + " sigma= " + 
						standardDeviation + "  array from hash " + 
						value.getHashfrequencies() + " length " + frTab.length + " zscore " + 
						value.getHashZscore() + " mean = " + value.getMean() + 
						" lastfr = " +value.getLastFrequency());
				System.out.println("------------------------------------------------------");*/
			}
		}
	}

	public static void calculStandardDeviationInHbase() throws IOException{
		@SuppressWarnings("deprecation")
		HBaseConfiguration config = new HBaseConfiguration();
		HTable tableFrequency = new HTable(config, "frequency");
		HTable tableZscore = new HTable(config, "zscore");
		ResultScanner scanner = tableFrequency.getScanner(new Scan());
		byte[] dateFamily = Bytes.toBytes("date");
		for (Result res = scanner.next(); res != null; res = scanner.next()) {
			int i =0;
			byte[] key = res.getRow();
			String lastDate = "";
			double [] frequencyTab = new double[res.size()];
			NavigableMap<byte[], byte[]> mapNav = res.getFamilyMap(dateFamily);
			for(Entry<byte[], byte[]> columnFamilyEntry : mapNav.entrySet()){
				byte[] columnMap = columnFamilyEntry.getValue();
				byte[] dateV = columnFamilyEntry.getKey();
				//System.out.println(Double.parseDouble(Bytes.toString(columnMap)));
				//System.out.println(Bytes.toString(dateV));
				lastDate = Bytes.toString(dateV);
				frequencyTab[i++] = Double.parseDouble(Bytes.toString(columnMap));
			}
			org.apache.commons.math.stat.descriptive.moment.StandardDeviation stDev = new StandardDeviation();
			org.apache.commons.math.stat.descriptive.moment.Mean stMean = new Mean();
			double mean = (stMean.evaluate(frequencyTab));

			double standardDeviation = stDev.evaluate(frequencyTab,mean);
			double zScore = 0;
			if(standardDeviation != 0)
				zScore = (frequencyTab[frequencyTab.length - 1] - mean) / (double)standardDeviation;
			//System.out.println(zScore);
			if(Math.abs(zScore) > 3.5)
				System.out.println(Bytes.toString(key));
			Put docPut = new Put(key);
			docPut.add(dateFamily, Bytes.toBytes(String.valueOf(lastDate)), Bytes.toBytes(String.valueOf(zScore)));
			tableZscore.put(docPut);
			i = 0;
		}
		tableFrequency.close();
		tableZscore.close();
	}


	public static void detectTrendInHbase(int startNbDays) throws IOException{
		@SuppressWarnings("deprecation")
		HBaseConfiguration config = new HBaseConfiguration();
		HTable tableFrequency = new HTable(config, "frequency");
		HTable tableZscore = new HTable(config, "zscore");
		HTable tableTrends = new HTable(config, "trends");
		ResultScanner scanner = tableFrequency.getScanner(new Scan());
		byte[] dateFamily = Bytes.toBytes("date");

		for (Result res = scanner.next(); res != null; res = scanner.next()) {
			int i =0;
			byte[] key = res.getRow();
			String lastDate = "";
			ArrayList<Double> freqArr = new ArrayList<Double>();
			NavigableMap<byte[], byte[]> mapNav = res.getFamilyMap(dateFamily);
			for(Entry<byte[], byte[]> columnFamilyEntry : mapNav.entrySet()){
				byte[] columnMap = columnFamilyEntry.getValue();
				byte[] dateV = columnFamilyEntry.getKey();
				//System.out.println(Double.parseDouble(Bytes.toString(columnMap)));
				//System.out.println(Bytes.toString(dateV));
				lastDate = Bytes.toString(dateV);
				freqArr.add(Double.parseDouble(Bytes.toString(columnMap)));

				if(i >= startNbDays){
					double[] currentFreq = arrayListToDouble(freqArr);
					org.apache.commons.math.stat.descriptive.moment.StandardDeviation stDev = new StandardDeviation();
					org.apache.commons.math.stat.descriptive.moment.Mean stMean = new Mean();
					double mean = (stMean.evaluate(currentFreq));

					double standardDeviation = stDev.evaluate(currentFreq,mean);
					double zScore = 0;
					if(standardDeviation != 0)
						zScore = (currentFreq[currentFreq.length - 1] - mean) / (double)standardDeviation;

					Put docPut = new Put(key);
					docPut.add(dateFamily, Bytes.toBytes(String.valueOf(lastDate)), Bytes.toBytes(String.valueOf(zScore)));
					tableZscore.put(docPut);
					if(Math.abs(zScore) > 3.5){
						System.out.println("W: " + Bytes.toString(key) + " | FREQ:" + currentFreq[currentFreq.length - 1] + " | DATE: " + String.valueOf(lastDate) + " | AVF: " + mean + " | SIG: " + standardDeviation + " | ZSCORE: " + zScore);
						Put trendPut = new Put(key);
						trendPut.add(dateFamily, Bytes.toBytes(String.valueOf(lastDate)), Bytes.toBytes(String.valueOf(zScore)));
						tableTrends.put(docPut);
					}
				}
				i++;
			}
			i = 0;
		}
		tableFrequency.close();
		tableZscore.close();
		tableTrends.close();
	}
	public static double calculMean(int sumFrequencies, int numberOfDocument){
		return sumFrequencies/(double)numberOfDocument;
	}

	public static double variance(Object[] objects) {
		long n = 0;
		double mean = 0;
		double s = 0.0;

		for (int i = 0; i < objects.length; i++) {
			n++;
			double delta = (Integer)objects[i] - mean;
			mean += delta / n;
			s += delta * ((Integer)objects[i] - mean);
		}
		return (s / n);
	}

	public static double[] arrayListToDouble(ArrayList<Double> arr){
		double[] ret = new double[arr.size()];
		Object[] arrO = arr.toArray();
		for(int i = 0; i < arr.size(); i++){
			ret[i] = (Double)arrO[i];
		}
		return ret;
	}
	public static double calculateMean(ArrayList<Double> arr){
		double[] currentFreq = Calculations.arrayListToDouble(arr);
		org.apache.commons.math.stat.descriptive.moment.Mean stMean = new Mean();
		return (stMean.evaluate(currentFreq));
	}

	public static double calculateVariance(ArrayList<Double> arr,double mean){
		double[] currentFreq = Calculations.arrayListToDouble(arr);
		org.apache.commons.math.stat.descriptive.moment.StandardDeviation stDev = new StandardDeviation();
		return stDev.evaluate(currentFreq,mean);
	}

	public static double calculateZscore(double x,double mean, double sigma){
		if(sigma == 0)
			return 0;
		return (x - mean) / sigma;
	}

	public static double daysDifferences(Date newD, Date oldD){
		if(newD == null || oldD == null)
			return 0;
		long mills_per_day = 1000 * 60 * 60 * 24; 
		double diff = ( newD.getTime() - oldD.getTime() ) / mills_per_day; 
		if(diff < 0){
			System.err.println("Invalid calcul for diff date");
			return 0;
		}
		return diff;
	}

	public static Date oldDateForTheOverallPeriod() throws IOException{
		@SuppressWarnings("deprecation")
		HBaseConfiguration config = new HBaseConfiguration();
		HTable date = new HTable(config, "date");
		return StringUtils.getDateFromString(date.getScanner(new Scan()).next().getRow());
	}

	public static double roundTwoDecimals(double d) {
		DecimalFormat twoDForm = new DecimalFormat("#.#########");
		return Double.valueOf(twoDForm.format(d));
	}
}
